/*
 * Copyright (C) 2025 Tsingteng MicroSystem
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file nci_tag_impl_tmsnci.cpp
 * @brief TAG相关接口实现
 */

#include "nfc_sdk_common.h"
#include "nci_tag_impl_tmsnci.h"
#include "nfc_log_api.h"
#include "nfa_discover.h"
#include "pac_map.h"
#include "taginfo.h"

namespace OHOS {
namespace NFC {
namespace NCI {

#define MAX_NUM_TECH_LIST 12
#define ISO_DEP_FRAME_MAX_LEN 261

#define RELEASE_RW_LOCK() nfa_rw_release_lock();
#define GET_TAG_WITH_RW_LOCK(_fail_action) \
    tNCI_STATUS ret = nfa_rw_request_lock(); \
    if (ret != NCI_STATUS_OK) { \
        NFCLOG_E("get rw lock failed, ret=%d", ret); \
        _fail_action; \
    } \
    tNCI_RW_TAG *p_tag = nfa_rw_get_activated_tag(); \
    if (!p_tag) { \
        NFCLOG_E("no tag activated"); \
        nfa_rw_release_lock(); \
        _fail_action; \
    }

NciTagImplTmsNci::NciTagImplTmsNci() {
}
NciTagImplTmsNci &NciTagImplTmsNci::GetInstance() {
    static NciTagImplTmsNci impl;
    return impl;
}
void NciTagImplTmsNci::SetTagListener(std::weak_ptr<ITagListener> listener) {
    this->mTagListener = listener;
}
std::vector<int> NciTagImplTmsNci::GetTechList(uint32_t tagDiscId) {
    GET_TAG_WITH_RW_LOCK(return {});
    std::vector<int> technologyList;
    for (uint8_t i = 0; i < p_tag->tech_size; i++) {
        technologyList.push_back((int) p_tag->tag_types[i]);
    }
    RELEASE_RW_LOCK();
    return technologyList;
}
uint32_t NciTagImplTmsNci::GetConnectedTech(uint32_t tagDiscId) {
    GET_TAG_WITH_RW_LOCK(return NCI_RW_TAG_TYPE_UNKNOWN);
    uint32_t connectedTechnolog = p_tag->tag_types[p_tag->index];
    RELEASE_RW_LOCK();
    return connectedTechnolog;
}
std::vector<AppExecFwk::PacMap> NciTagImplTmsNci::GetTechExtrasData(uint32_t tagDiscId) {
    GET_TAG_WITH_RW_LOCK(return {});
    std::vector<AppExecFwk::PacMap> tagTechExtras;
    for (uint8_t i = 0; i < p_tag->tech_size; i++) {
        tNCI_RW_POLL_DATA *p_poll = &p_tag->poll_data[i];
        tNCI_RW_ACT_DATA *p_act = &p_tag->act_data[i];
        AppExecFwk::PacMap pacMap;
        switch (p_tag->tag_types[i])
        {
        case NCI_RW_TAG_TYPE_A:
        case NCI_RW_TAG_TYPE_M1:
            if (p_act && p_act->len > 0) {
                uint32_t sak = p_act->data[0];
                pacMap.PutIntValue(KITS::TagInfo::SAK, sak);
            }
            if (p_poll) {
                std::string atqa = KITS::NfcSdkCommon::BytesVecToHexString(p_poll->data, p_poll->len);
                pacMap.PutStringValue(KITS::TagInfo::ATQA, atqa);
            }
            break;
        case NCI_RW_TAG_TYPE_B:
            if (p_poll && p_poll->len >= 7) {
                std::string appData = KITS::NfcSdkCommon::BytesVecToHexString(p_poll->data, 4);
                std::string protoInfo = KITS::NfcSdkCommon::BytesVecToHexString(p_poll->data + 4, 3);
                pacMap.PutStringValue(KITS::TagInfo::APP_DATA, appData);
                pacMap.PutStringValue(KITS::TagInfo::PROTOCOL_INFO, protoInfo);
            }
            break;
        case NCI_RW_TAG_TYPE_ISO_DEP:
            if (p_act) {
                bool is_type_a = false;
                for (uint8_t i = 0; i < p_tag->tech_size; i++) {
                    if (p_tag->tag_types[i] == NCI_RW_TAG_TYPE_A) {
                        is_type_a = true;
                        break;
                    }
                }
                std::string act_str = KITS::NfcSdkCommon::BytesVecToHexString(p_act->data, p_act->len);
                if (is_type_a) {
                    pacMap.PutStringValue(KITS::TagInfo::HISTORICAL_BYTES, act_str);
                } else {
                    pacMap.PutStringValue(KITS::TagInfo::HILAYER_RESPONSE, act_str);
                }
            }
            break;
        case NCI_RW_TAG_TYPE_F:
            if (p_poll && p_poll->len >= 8) {
                std::string pmn = KITS::NfcSdkCommon::BytesVecToHexString(p_poll->data, 8);
                pacMap.PutStringValue(KITS::TagInfo::NFCF_PMM, pmn);
                if (p_poll->len == 10) {
                    std::string sc = KITS::NfcSdkCommon::BytesVecToHexString(p_poll->data+8, 2);
                    pacMap.PutStringValue(KITS::TagInfo::NFCF_SC, sc);
                }
            }
            break;
        case NCI_RW_TAG_TYPE_V:
            if (p_poll && p_poll->len >= 2) {
                pacMap.PutIntValue(KITS::TagInfo::RESPONSE_FLAGS, p_poll->data[0]);
                pacMap.PutIntValue(KITS::TagInfo::DSF_ID, p_poll->data[1]);
            }
            break;
        case NCI_RW_TAG_TYPE_MUL:
            if (nfa_rw_is_ultralight_c()) {
                pacMap.PutBooleanValue(KITS::TagInfo::MIFARE_ULTRALIGHT_C_TYPE, true);
            } else {
                pacMap.PutBooleanValue(KITS::TagInfo::MIFARE_ULTRALIGHT_C_TYPE, false);
            }
            break;
        case NCI_RW_TAG_TYPE_NDEF:
            pacMap = mNdefExtras.Clone();
            mNdefExtras.Clear();
        default:
            break;
        }
        tagTechExtras.push_back(pacMap);
    }
    RELEASE_RW_LOCK();
    return tagTechExtras;
}
std::string NciTagImplTmsNci::GetTagUid(uint32_t tagDiscId) {
    GET_TAG_WITH_RW_LOCK(return {});
    std::string uid = KITS::NfcSdkCommon::BytesVecToHexString(p_tag->uid, p_tag->uid_len);
    RELEASE_RW_LOCK();
    return uid;
}
bool NciTagImplTmsNci::Connect(uint32_t tagDiscId, uint32_t technology) {
    GET_TAG_WITH_RW_LOCK(return false);
    int index = -1;
    for (uint8_t i = 0; i < p_tag->tech_size; i++) {
        if (technology == ((uint32_t) p_tag->tag_types[i])) {
            index = i;
            break;
        }
    }
    RELEASE_RW_LOCK();
    if (index < 0) {
        NFCLOG_E("technology=%d not found on curr tag", technology)
        return false;
    }
    if (technology == NCI_RW_TAG_TYPE_NDEF || technology == NCI_RW_TAG_TYPE_NDEF_FORMATABLE) {
        index = 0;
    }
    ret = nfa_rw_connect(index);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_connect index=%d failed, ret=%d", index, ret);
    }
    return ret == NCI_STATUS_OK;
}
bool NciTagImplTmsNci::Disconnect(uint32_t tagDiscId) {
    tNCI_STATUS ret = nfa_rw_disconnect();
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_disconnect failed, ret=%d", ret);
    }
    return ret == NCI_STATUS_OK;
}
bool NciTagImplTmsNci::Reconnect(uint32_t tagDiscId) {
    tNCI_STATUS ret = nfa_rw_reconnect();
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_reconnect failed, ret=%d", ret);
    }
    return ret == NCI_STATUS_OK;
}
int NciTagImplTmsNci::Transceive(uint32_t tagDiscId, const std::string &command, std::string &response) {
    uint16_t length = KITS::NfcSdkCommon::GetHexStrBytesLen(command);
    std::vector<uint8_t> commandVec;
    KITS::NfcSdkCommon::HexStringToBytes(command, commandVec);
    tNCI_RSP rsp = {0};
    tNCI_STATUS ret = nfa_rw_transceive(static_cast<uint8_t *>(commandVec.data()), length, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_transceive failed, ret=%d", ret);
        return -1;
    }
    response = KITS::NfcSdkCommon::BytesVecToHexString(rsp.data, rsp.len);
    nfa_utils_free_rsp(&rsp, true);
    return ret;
}
std::string NciTagImplTmsNci::ReadNdef(uint32_t tagDiscId) {
    tNCI_RSP rsp = {0};
    tNCI_STATUS ret = nfa_rw_ndef_read(&rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_ndef_read failed, ret=%d", ret);
        return {};
    }
    std::string ndef = KITS::NfcSdkCommon::BytesVecToHexString(rsp.data, rsp.len);
    nfa_utils_free_rsp(&rsp, true);
    return ndef;
}
std::string NciTagImplTmsNci::FindNdefTech(uint32_t tagDiscId) {
    tNCI_RSP ndef_rsp = {0};
    tNCI_RW_NDEF_TYPE ndef_type = NCI_RW_NDEF_TYPE_UNKNOWN;
    tNCI_RW_NDEF_MODE ndef_mode = NCI_RW_NDEF_MODE_UNKNOWN;
    tNCI_STATUS ret = nfa_rw_ndef_find_and_add_tech(&ndef_rsp, &ndef_type, &ndef_mode);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("no ndef msg read ret=%d", ret);
        return {};
    }
    mNdefExtras.Clear();
    std::string ndef_msg = "";
    if (ndef_rsp.data && ndef_rsp.len > 0) {
        ndef_msg = KITS::NfcSdkCommon::BytesVecToHexString(ndef_rsp.data, ndef_rsp.len);
    }

    mNdefExtras.PutStringValue(KITS::TagInfo::NDEF_MSG, ndef_msg);
    mNdefExtras.PutIntValue(KITS::TagInfo::NDEF_FORUM_TYPE, ndef_type);
    mNdefExtras.PutIntValue(KITS::TagInfo::NDEF_TAG_LENGTH, ndef_rsp.len);
    mNdefExtras.PutIntValue(KITS::TagInfo::NDEF_TAG_MODE, ndef_mode);
    return ndef_msg;
}
bool NciTagImplTmsNci::WriteNdef(uint32_t tagDiscId, std::string &command) {
    uint16_t length = KITS::NfcSdkCommon::GetHexStrBytesLen(command);
    std::vector<uint8_t> commandVec;
    KITS::NfcSdkCommon::HexStringToBytes(command, commandVec);
    tNCI_STATUS ret = nfa_rw_ndef_write(static_cast<uint8_t *>(commandVec.data()), length);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_ndef_write failed, ret=%d", ret);
    }
    return ret == NCI_STATUS_OK;
}
bool NciTagImplTmsNci::FormatNdef(uint32_t tagDiscId, const std::string &key) {
    tNCI_STATUS ret = nfa_rw_ndef_format(NULL, 0);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_ndef_format failed, ret=%d", ret);
    }
    return ret == NCI_STATUS_OK;
}
bool NciTagImplTmsNci::CanMakeReadOnly(uint32_t ndefType) {
    switch (ndefType) {
        case KITS::EmNfcForumType::NFC_FORUM_TYPE_1:
        case KITS::EmNfcForumType::NFC_FORUM_TYPE_2:
        case KITS::EmNfcForumType::NFC_FORUM_TYPE_3:
        case KITS::EmNfcForumType::NFC_FORUM_TYPE_4:
        case KITS::EmNfcForumType::ICODE_SLI:
            return true;
        default:
            return false;
    }
}
bool NciTagImplTmsNci::SetNdefReadOnly(uint32_t tagDiscId) {
    tNCI_STATUS ret = nfa_rw_set_tag_read_only(true);
    if (ret == NCI_STATUS_EXT_REJECTED) {
        ret = nfa_rw_set_tag_read_only(false);
    }
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_set_tag_read_only failed, ret=%d", ret);
    }
    return ret == NCI_STATUS_OK;
}
bool NciTagImplTmsNci::DetectNdefInfo(uint32_t tagDiscId, std::vector<int> &ndefInfo) {
    tNCI_RW_NDEF_INFO ndef_info = {0};
    tNCI_STATUS ret = nfa_rw_ndef_detect(&ndef_info);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_ndef_detect failed, ret=%d", ret);
        return false;
    }
    ndefInfo.push_back(ndef_info.max_size);
    ndefInfo.push_back(ndef_info.mode);
    return true;
}
bool NciTagImplTmsNci::IsTagFieldOn(uint32_t tagDiscId) {
    GET_TAG_WITH_RW_LOCK(return false);
    bool is_tag_field_on = (p_tag != NULL);
    RELEASE_RW_LOCK();
    return is_tag_field_on;
}
void NciTagImplTmsNci::StartFieldOnChecking(uint32_t tagDiscId, uint32_t delayedMs) {
    tNCI_STATUS ret = nfa_rw_watch_dog_start(delayedMs);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_watch_dog_start failed, ret=%d", ret);
    }
}
void NciTagImplTmsNci::StopFieldChecking() {
    tNCI_STATUS ret = nfa_rw_watch_dog_stop();
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_watch_dog_stop failed, ret=%d", ret);
    }
}
void NciTagImplTmsNci::SetTimeout(uint32_t tagDiscId, uint32_t timeout, uint32_t technology) {
    tNCI_STATUS ret = nfa_rw_set_transceive_timeout(technology, timeout);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("nfa_rw_set_transceive_timeout failed, ret=%d", ret);
    }
}
void NciTagImplTmsNci::GetTimeout(uint32_t tagDiscId, uint32_t &timeout, uint32_t technology) {
    timeout = nfa_rw_get_transceive_timeout(technology);
}
void NciTagImplTmsNci::ResetTimeout(uint32_t tagDiscId) {
    nfa_rw_reset_all_transceive_timeout();
}
uint32_t NciTagImplTmsNci::GetIsoDepMaxTransceiveLength() {
    return nfa_rw_get_iso_dep_max_transceive_length();
}
bool NciTagImplTmsNci::IsExtendedLengthApduSupported() {
    return nfa_rw_get_iso_dep_max_transceive_length() > ISO_DEP_FRAME_MAX_LEN;
}
uint16_t NciTagImplTmsNci::GetTechMaskFromTechList(const std::vector<uint32_t> &discTech) {
    uint16_t techMask = 0;
    size_t discTechLen = discTech.size();
    if (discTechLen > MAX_NUM_TECH_LIST) {
        NFCLOG_E("GetTechMaskFromTechList: invalid discTech length");
        return techMask;
    }
    for (uint16_t i = 0; i < discTechLen ; i++) {
        switch (discTech[i]) {
            case static_cast<int32_t>(KITS::TagTechnology::NFC_A_TECH):
                techMask |= NCI_RF_DISCOVER_POLL_A;
                break;
            case static_cast<int32_t>(KITS::TagTechnology::NFC_B_TECH):
                techMask |= NCI_RF_DISCOVER_POLL_B;
                break;
            case static_cast<int32_t>(KITS::TagTechnology::NFC_F_TECH):
                techMask |= NCI_RF_DISCOVER_POLL_F;
                break;
            case static_cast<int32_t>(KITS::TagTechnology::NFC_V_TECH):
                techMask |= NCI_RF_DISCOVER_POLL_V;
                break;
            default:
                break;
        }
    }
    return techMask;
}
std::string NciTagImplTmsNci::GetVendorBrowserBundleName() {
    return "";
}

void NciTagImplTmsNci::RwEventCallback(tNCI_RW_EVT event, const tNCI_RW_TAG *p_tag) {
    NFCLOG_I("enter: event=%d", event);
    if (mTagListener.expired()) {
        NFCLOG_E("mTagListener is null");
        return;
    }
    switch (event) {
        case NCI_RW_EVT_TAG_DETECTED:
            mTagListener.lock()->OnTagDiscovered(p_tag->disc_ids[0]);
            break;
        case NCI_RW_EVT_TAG_LOSTED:
            // TODO
            mTagListener.lock()->OnTagLost(0);
            break;
        default:
            NFCLOG_E("unknown event=%d", event);
            break;
    }
}

} // namespace NCI
} // namespace NFC
} // namespace OHOS